"""

2024-06-26 F 所有自定表单控件

"""

from PySide6.QtWidgets import QLineEdit, QComboBox, QCheckBox, QWidget, QHBoxLayout, QRadioButton, QPlainTextEdit, QTextEdit, QLabel, QVBoxLayout, QBoxLayout  # 如果您使用的是PyQt5
from PySide6.QtCore import Qt, QTimer
from PySide6 import QtCore
from PySide6.QtGui import QIntValidator, QSyntaxHighlighter, QTextCharFormat
## from PySide6.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QHBoxLayout, QGridLayout, QPushButton, QWidget
from PySide6.QtWidgets import QApplication, QTextEdit, QCompleter
from PySide6.QtGui import QColor, QTextCharFormat, QTextCursor
import re
from PySide6.QtCore import QRegularExpression, Qt
import time


from top.wardyan.core.vo.const import *

CONTROL_HEIGHT : int = 30
CONTROL_LWIDTH : int = 100
CONTROL_RWIDTH : int = 300





class KeywordHighlighter(QSyntaxHighlighter):
    def __init__(self, document, keywords, parent=None):
        super().__init__(document)
        self.keywords = keywords
        self.highlightingRules = []

        # 为每个关键字创建一个正则表达式和对应的格式
        keywordFormat = QTextCharFormat()
        keywordFormat.setForeground(Qt.blue)
        for keyword in keywords:
            patternStr = "(?i)\\b" + QRegularExpression.escape(keyword) + "\\b"
            pattern = QRegularExpression(patternStr)
            self.highlightingRules.append((pattern, keywordFormat))

    def highlightBlock(self, text):
        for pattern, format in self.highlightingRules:
            iterator = pattern.globalMatch(text)
            while iterator.hasNext():
                match = iterator.next()
                start = match.capturedStart()
                length = match.capturedLength()
                self.setFormat(start, length, format)
## LINEEDIT
class WLineEdit(QLineEdit):
    """
    2024-06-23
    """
    def __init__(self, value : str = "", parent=None, onchange=None):
        super().__init__(parent)
        self.focus_in_reason = None
        if value is None: value = ""
        self.setText(value)
        if onchange is not None:
            self.textChanged.connect(onchange)
    def focusInEvent(self, event):
        super().focusInEvent(event)
        self.selectAll()
        self.focus_in_reason = event.reason()
    
    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if self.focus_in_reason == Qt.MouseFocusReason:
            self.selectAll()
            self.focus_in_reason = None



class WIntegerLineEdit(WLineEdit):
    """
    2024-06-23
    """
    def __init__(self, value : int = 0, parent=None, onchange = None):
        super().__init__(parent, str(value), onchange=onchange)
        self.setValidator(QIntValidator())  # 使用整数验证器辅助限制输入
        self.textChanged.connect(self.check_and_correct_input)  # 连接文本变化信号到检查函数

    
    def check_and_correct_input(self):
        """Check the current text and remove any non-digit characters."""
        cleaned_text = ''.join(filter(str.isdigit, self.text()))  # 仅保留数字字符
        if cleaned_text != self.text():
            self.setText(cleaned_text)  # 如果有非数字被删除，则更新文本内容
            self.setCursorPosition(len(cleaned_text))  # 将光标置于文本末尾

    def keyPressEvent(self, event):
        """Override to ignore events that don't lead to valid input."""
        if not event.text().isdigit() and not (
                event.key() in [Qt.Key_Backspace, Qt.Key_Delete, 
                                Qt.Key_Left, Qt.Key_Right]
                or event.modifiers() & Qt.KeyboardModifier.ControlModifier  # Allow e.g., Ctrl+V for paste
        ):
            event.ignore()
        else:
            super().keyPressEvent(event)

    def insert(self, text):
        """Override to filter out non-numeric content when pasting."""
        if text.isdigit():
            super().insert(text)

    # 注意：对于paste操作，PyQt5中没有直接覆写的paste方法，因为paste通常是通过快捷键或上下文菜单触发的，
    # 并直接调用QClipboard的text()然后调用insert方法。
    # 上述insert方法的覆写已经处理了粘贴逻辑，确保只有数字能被插入。
    # def keyPressEvent(self, event):
    #     if not event.text().isdigit() and not (event.key() in [Qt.Key_Backspace, Qt.Key_Delete, Qt.Key_Left, Qt.Key_Right]):
    #         event.ignore()
    #     else:
    #         super().keyPressEvent(event)




## 下拉框
class WComboBox(QComboBox):
    """
    2024-06-23
    """
    def __init__(self, parent=None, onchange=None):
        super().__init__(parent)
        if onchange is not None:
            self.currentIndexChanged.connect(onchange)

    def setItem(self, items : dict) :
        self.items = items
        if items is not None :
            self.clear()
            for k, v in items.items() :
                self.addItem(k, v)

        
    def getSelectedValue(self) :
        """TODO"""
        return self.currentData()

    def setItemValue(self, value) :
        txt = ""
        vv = str(value)
        for k, v in self.items.items() :
            if str(v) == vv :
                txt = k
                break
        if txt is not None and txt != "" :
            self.setCurrentText(txt)


## 复选框
class WCheckBox(QCheckBox):
    """
    2024-06-23
    """
    def __init__(self, parent=None, onchange=None):
        super().__init__(parent)
        self.setTristate(False)
        self.setCheckState(Qt.Unchecked)
        if onchange is not None:
            self.stateChanged.connect(onchange)
        







class WPlainTextEdit (QPlainTextEdit) :
    def __init__(self, parent=None, onchange=None):
        super().__init__(parent)
        if onchange is not None :
            self.textChanged.connect(onchange)




class WTextEdit (QTextEdit) :
    def __init__(self, parent=None, onchange=None):
        super().__init__(parent)
        if onchange is not None :
            self.textChanged.connect(onchange)


# class WCodeEdit (QTextEdit) :
#     def __init__(self, parent=None):
#         super().__init__(parent)
#         self.highlightingRules = [(r'\bif\b|\belse\b|\bfor\b|\bin\b|\breturn\b', QColor('blue'))]  # 定义关键字及其颜色

#         self.textChanged.connect(self.onTextChanged)

    
#     def delayedHighlight(self):
#         # 使用单次触发的定时器来延迟执行高亮逻辑，避免循环调用
#         QTimer.singleShot(0, self.highlightKeywords)
#     def highlightKeywords(self, text):
#         ##print(text)
        
#         wasBlocked = self.blockSignals(True)  # 阻止信号发出
#         cursor = self.textCursor()
#         cursor.beginEditBlock()

#         format = QTextCharFormat()
#         format.setForeground(QColor('black'))  # 设置默认文本颜色
#         cursor = self.textCursor()
#         cursor.beginEditBlock()
#         cursor.select(QTextCursor.Document)
#         cursor.mergeCharFormat(format)
#         for pattern, color in self.highlightingRules:
#             expression = re.compile(pattern)
#             index = expression.search(text)
#             while index:
#                 length = len(index.group(0))
#                 format = QTextCharFormat()
#                 format.setForeground(color)
#                 cursor.setPosition(index.start())
#                 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, length)
#                 cursor.mergeCharFormat(format)
#                 index = expression.search(text, index.end())
#         cursor.endEditBlock()
        
#         self.blockSignals(wasBlocked)  # 恢复信号状态

#     def insertPlainText(self, text):
#         super().insertPlainText(text)
#         self.highlightKeywords(text)

#     def onTextChanged(self):
#         # 当文本改变时，调用highlightKeywords
#         self.highlightKeywords(self.toPlainText())



class WCodeEdit(QTextEdit):
    """
    SQL代码编辑器
    """
    def __init__(self, parent=None, highlightkeywords = []):
        super().__init__(parent)
        # 定义代码提示词汇列表
        self.completionList = ["SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE"]
        self.completionPrefix = ''
        self.keywords = []
        self.keywords.extend(CONST_SQLKEYWORDS)
        if highlightkeywords is not None and len(highlightkeywords) > 0 :
            self.keywords.extend(highlightkeywords)

        
        self.highlighter = KeywordHighlighter(self.document(), self.keywords)

    # def activeIt(self) :
    #     if not self.initHighlighted :
    #         self.highlighter = KeywordHighlighter(self.document(), self.keywords)
    def insertPlainText(self, text):
        super().insertPlainText(text)
    def keyPressEvent(self, event):
        # 处理Tab键用于代码补全
        if event.key() == Qt.Key_Tab:
            prefix = self.getCompletionPrefix()
            if prefix:
                for word in self.completionList:
                    if word.startswith(prefix):
                        self.insertPlainText(word[len(prefix):])
                        break
            return
        super().keyPressEvent(event)

    def getCompletionPrefix(self):
        cursor = self.textCursor()
        cursor.movePosition(QTextCursor.WordLeft, QTextCursor.KeepAnchor)
        prefix = cursor.selectedText()
        if not prefix:
            cursor.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor)
            prefix = cursor.selectedText()
        return prefix.strip()



# class WCodeEdit(QTextEdit):
#     def __init__(self, parent=None, highlightkeywords=[]):
#         super().__init__(parent)
#         self.completionList = ["SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE"]
#         self.completer = QCompleter(self.completionList, self)
#         self.completer.setCaseSensitivity(Qt.CaseInsensitive)
#         self.completer.setWidget(self)
#         self.completer.activated.connect(self.insertCompletion)
#     def insertCompletion(self, completion):
#         tc = self.textCursor()
        
#         # Select the entire word under the cursor
#         tc.select(QTextCursor.WordUnderCursor)
        
#         # Get the selected text (the current word)
#         current_word = tc.selectedText()
        
#         # Check if the completionPrefix is a prefix of the current word
#         if self.completionPrefix == current_word[:len(self.completionPrefix)]:
#             # Remove the current word
#             tc.removeSelectedText()
            
#             # Insert the completion
#             tc.insertText(completion)
            
#             # Calculate the new position for the cursor
#             # Note that we use the length of the current_word instead of self.completionPrefix
#             new_pos = tc.position() + len(completion) - len(current_word)
            
#             # Move the cursor to the new position
#             tc.setPosition(new_pos)
            
#             # Set the text cursor to the new position
#             self.setTextCursor(tc)
#     def textUnderCursor(self):
#         tc = self.textCursor()
#         tc.select(QTextCursor.WordUnderCursor)
#         return tc.selectedText()
        
    # def keyPressEvent(self, event):
    #     if self.completer.popup().isVisible():
    #         if event.key() in (Qt.Key_Enter, Qt.Key_Return):
    #             self.completer.activated.emit(self.completer.currentCompletion())
    #             event.accept()
    #             return

    #     super().keyPressEvent(event)

    # def keyPressEvent(self, event):
    #     if self.completer.popup().isVisible():
    #         if event.key() in (Qt.Key_Enter, Qt.Key_Return):
    #             event.ignore()
    #             return

    #     self.completionPrefix = self.textUnderCursor()
    #     if self.completionPrefix:
    #         self.completer.setCompletionPrefix(self.completionPrefix)
    #         popup = self.completer.popup()
    #         popup.setCurrentIndex(
    #             self.completer.completionModel().index(0, 0)
    #         )
    #         cr = self.cursorRect()
    #         cr.setWidth(self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width())
    #         self.completer.complete(cr)

    #     super().keyPressEvent(event)










class WRadioButtonGroup (QWidget) :

    ## radions : list = []

    def __init__(self, parent=None, onchange=None) :
        super().__init__(parent)
        self.radions : list[QRadioButton] = []
        self.alignlayout : QHBoxLayout = QHBoxLayout()
        self.alignlayout.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
        self.setLayout(self.alignlayout)
        self.onchage = onchange

    def setWidth(self, width : int) :
        self.setFixedWidth(width)
    def setHeight(self, height : int) :
        self.setMinimumHeight(height)
        

    def setItem(self, items : dict) :
        if self.radions is not None and len(self.radions) > 0 :
            for n in self.radions :
                n.close()
            self.radions = []

        if items is not None :
            for k, v in items.items() :
                ## self.btngrpAlign.layout().addWidget(v)
                rbtnLeft : QRadioButton = QRadioButton(k)
                rbtnLeft.data = v
                ## rbtnLeft.setChecked(True)
                #self.rbtnLeft.setMinimumHeight(HEIGHT)
                ## rbtnLeft.setFixedWidth(TXTWIDTH)
                self.alignlayout.addWidget(rbtnLeft)
                self.radions.append(rbtnLeft)
                if self.onchage is not None :
                    rbtnLeft.toggled.connect(self.onchage)

    def getSelected(self) :
        for rbtn in self.radions :
            if rbtn.isChecked() :
                return rbtn.text(), rbtn.data
        return None

    def setChecked(self, key) :
        for rbtn in self.radions :
            if rbtn.text() == key :
                rbtn.setChecked(True)
        

class WBitCheckGroup (QWidget) :
    """
    BIT勾选组
    """
    def __init__(self, items : dict, align : int = 0, onchange=None) :
        """
        items : dict = {值,显示}
        align : 0横向, 1纵向
        """
        super().__init__(None)

        self.items = items
        self.align = align
        self.onchange = onchange

        self.btns = []

        self.init()



    def init(self) :

        layout : QBoxLayout = None

        if self.align == 1 :
            ## 纵向
            layout = QVBoxLayout()
        else :
            layout = QHBoxLayout()
        self.setLayout(layout)
        ## 
            
        if self.items is not None :
            for k, v in self.items.items() :
                ## self.addItem(k, v)
                """
                self.chkIsDedicate : WCheckBox = WCheckBox()
                self.chkIsDedicate.setText("专用")
                self.chkIsDedicate.setChecked(self.warning["xIsDedicate"])
                """
                chk : WCheckBox = WCheckBox()
                chk.setText(v)
                chk.data = int(k)
                if self.onchange is not None :
                    chk.stateChanged.connect(self.onchange)
                layout.addWidget(chk)
                self.btns.append(chk)

    def setValue(self, value : int) :
        for chk in self.btns :
            data = chk.data
            ok = False
            if value & data > 0 :
                ok = True
            chk.setChecked(ok)

            
            
    def setReadOnly(self, readonly : bool) :
        for chk in self.btns :
            chk.setEnabled(not readonly)


    def getValue(self) :
        rs = 0
        for chk in self.btns :
            data = chk.data
            if chk.isChecked() :
                rs = rs | data
        return rs













































def add_line_edit_integer(label : str, editStr : str, HEIGHT : int = CONTROL_HEIGHT, LBLWIDTH : int = CONTROL_LWIDTH, TXTWIDTH : int = CONTROL_RWIDTH, onchange = None) :
    """
    生成一个标签, 一个行输入 (数值)
    """
    lblIdx : QLabel  = QLabel(label)
    lblIdx.setMinimumHeight(HEIGHT)
    lblIdx.setFixedWidth(LBLWIDTH)
    lblIdx.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    ##lytGrid.addWidget(lblIdx,0,0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    txtIdx : WIntegerLineEdit = WIntegerLineEdit(onchange = onchange)
    txtIdx.setMinimumHeight(HEIGHT)
    txtIdx.setFixedWidth(TXTWIDTH)
    txtIdx.setText(editStr)
    ## lytGrid.addWidget(self.txtIdx,0,1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
    return lblIdx, txtIdx
def add_line_edit(label : str, editStr : str, HEIGHT : int = CONTROL_HEIGHT, LBLWIDTH : int = CONTROL_LWIDTH, TXTWIDTH : int = CONTROL_RWIDTH, onchange = None) :
    """
    生成一个标签, 一个行输入
    """
    lblIdx : QLabel  = QLabel(label)
    lblIdx.setMinimumHeight(HEIGHT)
    lblIdx.setFixedWidth(LBLWIDTH)
    lblIdx.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    ##lytGrid.addWidget(lblIdx,0,0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    txtIdx : WLineEdit = WLineEdit(onchange = onchange)
    txtIdx.setMinimumHeight(HEIGHT)
    txtIdx.setFixedWidth(TXTWIDTH)
    txtIdx.setText(editStr)
    ## lytGrid.addWidget(self.txtIdx,0,1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
    return lblIdx, txtIdx



def add_commbobox(label : str, value, datas : dict, HEIGHT : int = CONTROL_HEIGHT, LBLWIDTH : int = CONTROL_LWIDTH, TXTWIDTH : int = CONTROL_RWIDTH, onchange = None) :
    """生成一个标签,一个下拉框"""
    lbl : QLabel  = QLabel(label)
    lbl.setMinimumHeight(HEIGHT)
    lbl.setFixedWidth(LBLWIDTH)
    lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    ##lytGrid.addWidget(lblIdx,0,0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    cmb : WComboBox = WComboBox(onchange = onchange)
    cmb.setMinimumHeight(HEIGHT)
    cmb.setFixedWidth(TXTWIDTH)
    cmb.setItem(datas)
    cmb.setItemValue(value)
    ## cmb.setText(editStr)
    ## lytGrid.addWidget(self.txtIdx,0,1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
    
    return lbl, cmb



def add_bitcheckgroup(label : str, value : int, datas : dict, align : int = 0, HEIGHT : int = CONTROL_HEIGHT, LBLWIDTH : int = CONTROL_LWIDTH, TXTWIDTH : int = CONTROL_RWIDTH, onchange = None) :
    """生成一个标签,一个下拉框"""
    lbl : QLabel  = QLabel(label)
    lbl.setMinimumHeight(HEIGHT)
    lbl.setFixedWidth(LBLWIDTH)
    lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    ##lytGrid.addWidget(lblIdx,0,0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
    st : WBitCheckGroup = WBitCheckGroup(datas, align,onchange = onchange)
    st.setMinimumHeight(HEIGHT)
    st.setFixedWidth(TXTWIDTH)
    # cmb : WComboBox = WComboBox()
    # cmb.setMinimumHeight(HEIGHT)
    # cmb.setFixedWidth(TXTWIDTH)
    # cmb.setItem(datas)
    # cmb.setItemValue(value)
    ## cmb.setText(editStr)
    ## lytGrid.addWidget(self.txtIdx,0,1, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
    if value is not None :
        st.setValue(value)
    
    return lbl, st


